package com.wiscamp.ninechapters.common.exception;

import com.wiscamp.ninechapters.common.core.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;

public class ExceptionHandler {
    private ExceptionHandler() {
    }

    private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);

    public static final String UNKNOWN_DATA_EXCEPTION_CODE = "data-exception.unknown";

    public static final String UNKNOWN_SERVICE_EXCEPTION_CODE = "service-exception.unknown";

    static final String DATA_EXCEPTION_PREFIX_LINE = "--- Data Exception Occurs ---";

    static final String SERVICE_EXCEPTION_PREFIX_LINE = "--- Service Exception Occurs ---";

    public static void logException(Exception ex, String prefixLine) {
        String stackTrace = getStackTrace(ex);
        logger.error(prefixLine);
        logger.error(stackTrace);
    }


    public static void logException(Exception ex, Logger logger, String prefixLine) {
        String stackTrace = getStackTrace(ex);
        logger.error(prefixLine);
        logger.error(stackTrace);
    }

    public static void throwDataException(Exception ex, String errorCode, Object data) {
        if (!Objects.isNull(logger)) {
            logException(ex, logger, DATA_EXCEPTION_PREFIX_LINE);
        }
        throw new DataException(ex, errorCode, data);
    }

    public static void throwDataException(Exception ex, Logger logger, String errorCode, Object data) {
        if (!Objects.isNull(logger)) {
            logException(ex, logger, DATA_EXCEPTION_PREFIX_LINE);
        }
        throw new DataException(ex, errorCode, data);
    }

    public static void throwServiceException(Exception ex, String errorCode, Object data) {
        if (!Objects.isNull(logger)) {
            logException(ex, logger, SERVICE_EXCEPTION_PREFIX_LINE);
        }
        throw new ServiceException(ex, errorCode, data);
    }

    public static void throwServiceException(Exception ex, Logger logger, String errorCode, Object data) {
        if (!Objects.isNull(logger)) {
            logException(ex, logger, SERVICE_EXCEPTION_PREFIX_LINE);
        }
        throw new ServiceException(ex, errorCode, data);
    }

    public static void throwAppServiceException(Exception ex, Logger logger, String errorCode, Object data) {
        if (!Objects.isNull(logger)) {
            logException(ex, logger, SERVICE_EXCEPTION_PREFIX_LINE);
        }
        throw new AppServiceException(ex, errorCode, data);
    }

    public static String getStackTrace(Exception ex) {
        var builder = new StringBuilder();
        //var lineSeparator = System.getProperty("line.separator");
        var lineSeparator = "   ";
        var code = "";
        var data = "";
        var exceptionType = "";
        var message = "";

        Exception innerException = null;
        if (ex instanceof DataException) {
            var dataException = (DataException) ex;
            code = dataException.getCode();
            var dataObject = dataException.getData();
            if (dataObject != null) data = JsonHelper.toJson(dataObject);
            if (dataException.getInnerException() != null) {
                innerException = dataException.getInnerException();
            }
        } else if (ex instanceof ServiceException) {
            var serviceException = (ServiceException) ex;
            code = serviceException.getCode();
            var dataObject = serviceException.getData();
            if (dataObject != null) data = JsonHelper.toJson(dataObject);
            if (serviceException.getInnerException() != null) {
                innerException = serviceException.getInnerException();
            }
        } else if (ex instanceof AppServiceException) {
            var appServiceException = (AppServiceException) ex;
            code = appServiceException.getCode();
            var dataObject = appServiceException.getData();
            if (dataObject != null) data = JsonHelper.toJson(dataObject);
            if (appServiceException.getInnerException() != null) {
                innerException = appServiceException.getInnerException();
            }
        } else {
            // current exception is Exception
            innerException = ex;
        }

        if (innerException == null)
            innerException = ex;

        exceptionType = innerException.getClass().getSimpleName();
        message = innerException.getMessage();
        builder.append("Exception: " + exceptionType + lineSeparator);
        builder.append("Message: " + message + lineSeparator);

        StackTraceElement[] elements = innerException.getStackTrace();
        builder.append("Trace: " + lineSeparator);
        for (int i = 0; i <= elements.length - 1; i++) {
            String fileName = elements[i].getFileName();
            String className = elements[i].getClassName();
            if (!className.contains("com.wiscamp"))
                continue;
            String methodName = elements[i].getMethodName();
            int line = elements[i].getLineNumber();
            if (fileName == null) continue;
            String text = "    at " + methodName + " (" + fileName + " : " + line + ")";
            if (text.contains("<generated>")) continue;
            builder.append(text + lineSeparator);
        }
        return builder.toString();
    }
}
